#define CALLHANDLERERROR_KLUGE

using System;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Diagnostics;
using System.Reflection;
using System.Collections;

//
// TODO
// There's also only one global flag for whether we're single stepping.
// Basically, you can have multiple listeners and threads, but God
// help you if you turn on single stepping when there's more than
// one listener.
//
namespace Meta {
	public delegate object Thunk();
	public delegate void VoidThunk();

	public class TopLevel {
		public static IGUIInterface gui;
		public static Writer defaultReplOut;
		public static SomewhatRichTextWriter defaultRawOut;
#if THREADSAFE
		[ThreadStatic]
		static TopLevel currentThreadTopLevel;
		public static TopLevel CurrentThreadTopLevel {
			get {
				// Reading thread-local storage is slow, so we cache it.
				TopLevel probe = currentThreadTopLevel;
				if (probe==null) {
					TopLevel tl = new TopLevel();
					currentThreadTopLevel = tl;
					tl.thread = Thread.CurrentThread;
					return tl;
				}
				return probe;
			}
		}
#else
		static TopLevel theTopLevel = new TopLevel();
		public static TopLevel CurrentThreadTopLevel {
			get {
					return theTopLevel;
				}
		}
#endif

		public static void RunStandAlone(string loadFile) {
			standAloneMode = true;
			gui = new StubGUIInterface();
			Load(loadFile, Namespace.UserNamespace);
		}

		public static bool standAloneMode = false;
		public static event VoidThunk idle;          // Called when all user threads terminate
		public static event VoidThunk evalStart;  // Called at the start of an eval command
		public static event VoidThunk evalEnd;   // Called at the end of an eval command
		public static Symbol eval = Symbol.Intern("eval");
		//public static Parser parser=new Parser();
		public static bool lowLevelDebug=false;
        public static ExecutionMode GlobalExecutionMode;
        static bool m_traceLoading;
        public static bool AutoCheck = false;
        static bool alwaysOpen2D = false;

        // Instance fields
		public SomewhatRichTextWriter rawOut = defaultRawOut;
		public Writer replOut = defaultReplOut;
		public Thread thread = Thread.CurrentThread;
		public Inspector debugger;  // Current inspector used for showing stack during breakpointed execution
		public bool isDying = false;
		EvalStack evalStack;
        

		public static OutputWindow MakeOutputWindow() {
			return gui.MakeOutputWindow();
		}

		public static void SetStatusMessage(string message) {
			if (gui!=null)
				gui.SetStatusMessage(message);
		}

		public static bool TraceLoading {
			get {
				return m_traceLoading;
			}
			set {
				m_traceLoading = value;
			}
		}

		public static bool SingleStepMode {
			get {
				return GlobalExecutionMode.Breakpoint;
			}
			set {
				GlobalExecutionMode.Breakpoint = value;
			}
		}

		public static bool AlwaysOpen2D {
			get {
				return alwaysOpen2D;
			}
			set {
				alwaysOpen2D = value;
				if (alwaysOpen2D)
					OpenBasic2D();
			}
		}

		public const char promptCarret = '\u25ba';
		public const string promptString = "\u25ba ";

		public const string minusSymbol = "\u2212";
		public const string divideSymbol = "\u00f7";
		public const string divisionSlashSymbol = "\u2215";
		public const string fractionSlashSymbol = "\u2044";
		public const string divisionSymbol = fractionSlashSymbol;
		public const string timesSymbol = "\u00d7";
		static readonly Symbol sEval = Symbol.Intern("eval");
		internal static PackageDirectory thePackagesDirectory;

		public static readonly Fluid m_currentSourceFileToken = new Fluid("CurrentSourceFileToken", null);
		public static FileToken CurrentSourceFileToken {
			get { return (FileToken)m_currentSourceFileToken.Value;	}
			set {	m_currentSourceFileToken.Value = value; }
		}
		public static string CurrentSourceFile {
			get {
				FileToken t = CurrentSourceFileToken;
				if (t==null)
					return null;
				else
					return t.Path;
			}
		}
		public static readonly Fluid m_currentSourceDefinition = new Fluid("CurrentSourceDefinition", null);
		public static string CurrentSourceDefinition {
			get { return (string)m_currentSourceDefinition.Value; }
			set { m_currentSourceDefinition.Value = value; }
		}

		static TopLevel() {
			Symbol.Rename("<>", "\u25ca");
			Symbol.Rename("->", "\u2192");
			Symbol.Rename("symbol->string", "symbol\u2192string");
			Symbol.Rename("string->symbol", "string\u2192symbol");
			Symbol.Rename("procedure->sound", "procedure\u2192sound");
			Symbol.Rename("<-", "\u2190");
			Symbol.Rename("-", minusSymbol);
			Symbol.Rename("*", timesSymbol);
			Symbol.Rename("/", divisionSymbol);
			Symbol.Rename("<=", "\u2264");
			Symbol.Rename(">=", "\u2265");
			Symbol.Rename("not-=", "\u2260");
			Symbol.Rename("pi", "\u03c0");
			Symbol.Rename("2pi", "2\u03c0");
			Symbol.Rename("pi/2", "\u03c0/2");
			Symbol.Rename("sqrt", "\u221a");
			//Symbol.Rename("eq?", "\u2261");
			Symbol.Rename("lambda", "\u03bb");
			//Symbol.Rename("not-==", "u2262");
			//Symbol.Rename("=*", "\u225b");
			Symbol.Rename("tree-equivalent?", "\u2248");
			Symbol.Rename("infinity", "\u221e");
			Symbol.Rename("==", "\u2261");
			// Need to preload the symbol table with these symbols or checkbinding will fail in MaybeAutoCheck
			Symbol.Intern("Packages");
			Symbol.Intern("Analysis");
			Symbol.Intern("check-namespace");
			
			SpecialForms.Load();
			Builtins.Load();
			Lambdas.Load();

			//
			// Graphics definitions common to both 2D and 3D
			//
			CommonGraphics.Load();
			Namespace.DefineManifestConstant("point", Namespace.CurrentNamespace.CheckBinding("vector"));
			Namespace.DefineManifestConstant("point-x", Namespace.CurrentNamespace.CheckBinding("vector-x"));
			Namespace.DefineManifestConstant("point-y", Namespace.CurrentNamespace.CheckBinding("vector-y"));
			Namespace.DefineManifestConstant("point-z", Namespace.CurrentNamespace.CheckBinding("vector-z"));
			Namespace.DefineManifestConstant("point?", Namespace.CurrentNamespace.CheckBinding("vector?"));

			Namespace.DefineManifestConstant("<>", null, "Holds the result of the last expression executed");
			Namespace.DefineManifestConstant("pi", System.Math.PI);
			Namespace.DefineManifestConstant("2pi", 2*System.Math.PI);
			Namespace.DefineManifestConstant("pi/2", System.Math.PI/2.0);
			Namespace.DefineManifestConstant("e", System.Math.E);
			Namespace.DefineManifestConstant("infinity", 1.0/0);
			Namespace.DefineManifestConstant("+", new PrimitiveProcedureAdd(Symbol.Intern("+")));
			Namespace.DefineManifestConstant("-", new PrimitiveProcedureSubtract(Symbol.Intern("-")));
			Namespace.DefineManifestConstant("*", new PrimitiveProcedureMultiply(Symbol.Intern("*")));
			Namespace.DefineManifestConstant("/", new PrimitiveProcedureDivide(Symbol.Intern("/")), "Floating-point division");
			Namespace.DefineManifestConstant("quotient", new PrimitiveProcedureQuotient(Symbol.Intern("quotient")), "Integer division");
			Namespace.DefineManifestConstant("min", new PrimitiveProcedureMin(Symbol.Intern("min")));
			Namespace.DefineManifestConstant("max", new PrimitiveProcedureMax(Symbol.Intern("max")));

			Namespace.DefineManifestConstant("Object", typeof(object), "The type of all data iterms");
			Namespace.DefineManifestConstant("Type", typeof(Type), "The data type of data types (objects that themselves represent types)");
			Namespace.DefineManifestConstant("Void", typeof(void), "The return value data type for procedures that don't return a value");
			Namespace.DefineManifestConstant("Integer", PseudoType.Integer, "The data type of integers");
			Namespace.DefineManifestConstant("Float", PseudoType.Float, "The data type of all floating-point numbers.");
			Namespace.DefineManifestConstant("Number", PseudoType.Number, "The data type of numbers.");
			Namespace.DefineManifestConstant("Arithmetic", PseudoType.Arithmetic, "The supertype of all objects on which arithmetic can be performed.");
			Namespace.DefineManifestConstant("Vector", PseudoType.Vector, "The supertype of all vectors.");
			Namespace.DefineManifestConstant("Vector2D", PseudoType.Vector2D, "The supertype of all 2D vectors.");
			Namespace.DefineManifestConstant("Boolean", typeof(bool), "The data type of Boolean (true/false) data");
			Namespace.DefineManifestConstant("Character", typeof(char), "The data type of single characters");
			//Namespace.DefineManifestConstant("Sequence", typeof(object[]), "The data type of sequences; this is also the .NET type \"Object[]\".");
			Namespace.DefineManifestConstant("Array", typeof(System.Array), "Data type of all arrays (the simplest kinds of lists).");
			Namespace.DefineManifestConstant("ArrayList", typeof(ArrayList), "A kind of list that can be dynamically modified.");
			Namespace.DefineManifestConstant("Hashtable", typeof(Hashtable), "A kind of dictionary.");
			Namespace.DefineManifestConstant("Dictionary", typeof(IDictionary), "The data type of all dictionaries.");
			Namespace.DefineManifestConstant("List", typeof(IList), "Data type of all lists.");
			Namespace.DefineManifestConstant("String", typeof(string), "The data type of character strings");
			Namespace.DefineManifestConstant("Procedure", typeof(Procedure), "The data type of procedures.");
			Namespace.DefineManifestConstant("Bitmap", typeof(System.Drawing.Bitmap), "The data type of Bitmaps.");
			Namespace.DefineManifestConstant("Graphics", typeof(System.Drawing.Graphics), "The data type of graphics devices (objects on which graphics can be drawn).");
			Namespace.DefineManifestConstant("Font", typeof(System.Drawing.Font), "The data type of fonts.");
			Namespace.DefineManifestConstant("Color", typeof(System.Drawing.Color), "The data type of color objects.");
			Namespace.DefineManifestConstant("Pen", typeof(System.Drawing.Pen), "The data type of pen objects.");
			Namespace.DefineManifestConstant("Brush", typeof(System.Drawing.Brush), "The data type of brush objects.");
			Namespace.DefineManifestConstant("Region", typeof(System.Drawing.Region), "The data type of Region objects.");
			Namespace.DefineManifestConstant("Path", typeof(System.IO.Path), "Contains constructors and accessors for file names and other path strings.");
			Namespace.DefineManifestConstant("Directory", typeof(System.IO.Directory), "Operations for creating, changing, and querying directories of files.");
			Namespace.DefineManifestConstant("DirectoryInfo", typeof(System.IO.DirectoryInfo), "Information about a directory.");
			Namespace.DefineManifestConstant("File", typeof(System.IO.File), "Operations for creating, changing, and querying files.");
			Namespace.DefineManifestConstant("FileInfo", typeof(System.IO.FileInfo), "Contains Information about a file.");
			Namespace.DefineManifestConstant("Convert", typeof(System.Convert), "Contains a number of methods for converting between numeric types.");
			Namespace.DefineManifestConstant("Assembly", typeof(System.Reflection.Assembly), "The type of .NET code assemblies (e.g. DLLs).");
			Namespace.DefineManifestConstant("Exception", typeof(Exception), "The base type of all Exception objects");
			Namespace.DefineManifestConstant("Math", typeof(System.Math), "The .NET Math package.");
			//Namespace.DefineManifestConstant("red", Color.Red, "The color object for red");
			//Namespace.DefineManifestConstant("green", Color.Green, "The color object for green");
			//Namespace.DefineManifestConstant("blue", Color.Blue, "The color object for blue");
			//Namespace.DefineManifestConstant("black", Color.Black, "The color object for black");
			//Namespace.DefineManifestConstant("white", Color.White, "The color object for white");
			//Namespace.DefineManifestConstant("gray", Color.Gray, "The color object for gray");
			Namespace.DefineManifestConstant("System", new NativeNamespace("System"), "The .Net System namespace, e.g. for System.Object");
			Namespace.DefineManifestConstant("Microsoft", new NativeNamespace("Microsoft"), "The .Net Microsoft namespace, e.g. for Microsoft.Win32");
			Namespace.DefineManifestConstant("Meta", new NativeNamespace("Meta"), "The native .Net namespace containing the class definitions of the Meta interpreter");
			Namespace.DefineManifestConstant("load", new LoadFileProc());
			Namespace.DefineManifestConstant("set-fields", new SetFieldsProcedure());
			Namespace.DefineManifestConstant("from-definition", new FromDefinitionParser());

			thePackagesDirectory = new PackageDirectory(Installation.installDirectory+Path.DirectorySeparatorChar+"Packages");
			PackageDirectory examples = new PackageDirectory(Installation.installDirectory+Path.DirectorySeparatorChar+"Examples");
			Namespace.DefineManifestConstant("Packages", thePackagesDirectory);
			Namespace.DefineManifestConstant("Examples", examples);
			Namespace.DefineManifestConstant("current-namespace", Namespace.currentNamespaceFluid, "Returns the namespace in which execution is currently taking place.");
			//Namespace.DefineManifestConstant("current-source-file", TopLevel.m_currentSourceFile, "Returns the current source file from which code is being loaded.");
			Namespace.DefineManifestConstant("in-namespace", new InNamespaceParser());
			Namespace.DefineManifestConstant("get", Namespace.RootNamespace.CheckFbinding("item"), "Returns the item at the specified position in the list.");

#if OPEN_GRAPHICS
			OpenBasic2D();
#endif

			SetSetter("get", "set-item");
			SetSetter("item", "set-item");
			SetSetter("lookup", "store-dictionary");
			SetSetter("setter", "set-setter");
			SetListSetter("first", 0);
			SetListSetter("second", 1);
			SetListSetter("third", 2);
			SetListSetter("fourth", 3);
			SetListSetter("fifth", 4);
			SetListSetter("sixth", 5);
			SetListSetter("seventh", 6);
			SetListSetter("eighth", 7);
			SetListSetter("ninth", 8);
			SetListSetter("tenth", 9);

			//
			// Load the Standard Library into RootNamespace.
			//
			try 
			{
				// Reverse commenting for force a load of the source version
				Loader.Load(Installation.installDirectory+Path.DirectorySeparatorChar+"Packages"+Path.DirectorySeparatorChar+"Standard library.dll");
				//Load(Installation.installDirectory+Path.DirectorySeparatorChar+"Packages"+Path.DirectorySeparatorChar+"Standard library.meta", Namespace.RootNamespace);
			} 
			catch
			{
			}
			Namespace.UserNamespace.Import(Namespace.RootNamespace);
			Namespace.currentNamespaceFluid.Value = Namespace.UserNamespace;
			if (alwaysOpen2D)
				OpenBasic2D();
		}

		public static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs ea) {
			Exception e = ea.ExceptionObject as Exception;

			if (e!=null && !(e is ThreadAbortException)) {
				CurrentThreadTopLevel.ShowException(e);
				TopLevel.CurrentThreadTopLevel.Kill();
			}
		}

		public static void ThreadExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs ea) {
			// Just rethrow the exception so that the surrounding handler from Meta grabs it.
			Exception e = ea.Exception as Exception;
			if (e!=null)
				throw e;
			else
				throw new Exception("Unknown native exception");
#if notdef
			if (System.Threading.Thread.CurrentThread.Name=="GUI Thread") {
				new System.Windows.Forms.ThreadExceptionDialog(ea.Exception).ShowDialog();
			} else {
				Exception e = ea.Exception;

				if (e!=null && !(e is ThreadAbortException)) {
					CurrentThreadTopLevel.ShowException(e);
					TopLevel.CurrentThreadTopLevel.Kill();
				}
			}
#endif
		}

		public static void InstallFormDebugger() {
            System.Windows.Forms.Application.ThreadException += new ThreadExceptionEventHandler(Meta.TopLevel.ThreadExceptionHandler);
		}

#if CALLHANDLERERROR_KLUGE
		static Exception callHandlerError;
		public static object CallHandler(Procedure p, object sender, object args) {
			object result = null;
			callHandlerError = null;
			try {
				result = Procedure.TracedCall(p, sender, args);
			} catch (Exception e) {
				callHandlerError = e;
				System.Threading.Thread.CurrentThread.Abort(e);
			}
			return result;
		}
#endif

		static void SetSetter(string proc, string setter) {
			((Procedure)Namespace.CurrentNamespace.CheckBinding(proc)).setter = (Procedure)Namespace.CurrentNamespace.CheckBinding((string)setter);
		}

		static void SetListSetter(string proc, int position) {
			((Procedure)Namespace.CurrentNamespace.CheckBinding(proc)).setter = new ListSetter(position, proc);
		}

		public TopLevel(SomewhatRichTextWriter output, IGUIInterface guiInterface) : this() {
			if (gui==null) {
				gui = guiInterface;
#if THREADSAFE
				rawOut = defaultRawOut = output;
				replOut = defaultReplOut = new Writer(output);
#else
			rawOut = theTopLevel.rawOut = output;
			replOut = theTopLevel.replOut = new Writer(output);
#endif
			}
		}

		public static void Log(params object[] stuff) {
			gui.Log(stuff);
		}

		public static void Log(object x) {
			gui.Log(x);
		}

		public TopLevel() {
            try
            {
                // We need to wrap this in a catch since it will throw an error if
                // it's run in the GUI thread (because it's already created a control)
                System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
            }
            catch { }
        }

		static TopLevel threadBeingDebugged = null;
		public static TopLevel ThreadBeingDebugged {
			get {
				if (threadBeingDebugged!=null && threadBeingDebugged.thread!=null && threadBeingDebugged.thread.IsAlive)
					return threadBeingDebugged;
				else
					return null;
			}
		}

		static void OpenBasic2D() {
			Namespace.UserNamespace.Import((Namespace)((GlobalVariable)thePackagesDirectory.Lookup(Symbol.Intern("Basic2D"), true, true, true)).value);
		}

		public static Inspector CurrentDebugger {
			get {
				if (threadBeingDebugged!=null)
					return threadBeingDebugged.debugger;
				else
					return null;
			}
		}

		static object temporaryBreakpoint=null;
		public void BreakPoint(Expression exp, Environment env) {
			evalStack = EvalStack.CurrentThreadEvalStack;
			evalStack.SetExpression(exp, env);
			evalStack.BreakpointFrame = false;
			threadBeingDebugged = this;
			isDying = false;
			if (temporaryBreakpoint!=null && (exp==temporaryBreakpoint || EvalStack.CurrentThreadEvalStack.CurrentProcedure==temporaryBreakpoint)) {
				ClearBreakpoint(temporaryBreakpoint);
				temporaryBreakpoint = null;
			}

			if (debugger != null && !debugger.IsDisposed) {
				debugger.ShowBreakpoint();
			} else
				Inspector.Debug();
			if (!standAloneMode) {
				Thread.CurrentThread.Suspend();
            }
			if (isDying) 
			{
				if (standAloneMode)
					System.Environment.Exit(1);
				else
					Thread.CurrentThread.Abort();
			}
		}

		public void Step() {
			threadBeingDebugged = null;
			TopLevel.SingleStepMode = true;
            if (thread != null)
    			thread.Resume();
		}

		public void Continue() {
			threadBeingDebugged = null;
			TopLevel.SingleStepMode = false;
            if (thread != null)
    			thread.Resume();
		}

		public void StepOver() {
			evalStack.BreakpointFrame = true;
			Continue();
		}

		public void ContinueUntil(object target) {
			threadBeingDebugged = null;
			TopLevel.SingleStepMode = false;
			if (temporaryBreakpoint!=null)
				ClearBreakpoint(temporaryBreakpoint);
			temporaryBreakpoint = target;
			SetBreakpoint(target);
            if (thread != null)
    			thread.Resume();
		}

		public void Kill() {
			if (!isDying) {
				isDying = true;
				if (thread != null && thread.IsAlive) {
					if (thread.ThreadState==System.Threading.ThreadState.Suspended)
						thread.Resume();  // Resume it so we can abort it.
					thread.Abort();
				}
			}
		}

		public void ShowEvalStack() {
			EvalStack.CurrentThreadEvalStack.InvalidateRenderCache();
			//Inspector.Inspect(EvalStack.CurrentThreadEvalStack);
			Inspector.Debug();
		}
		
		public void ShowException(Exception e) {
#if CALLHANDLERERROR_KLUGE
			if (e is ThreadAbortException && callHandlerError!=null) {
				e = callHandlerError;
				callHandlerError = null;
			}
#endif
			if (e is TargetInvocationException)
				e = ((TargetInvocationException)e).InnerException;
			threadBeingDebugged = this;
			EvalStack.CurrentThreadEvalStack.ForcePush(e, null);
			ShowEvalStack();
		}

        public static void CallThunkWithDebuggerSupport(Procedure p, bool rethrow)
        {
            try
            {
                p.Call();
            }
            catch (Exception e)
            {
                TopLevel.CurrentThreadTopLevel.ShowException(e);
                if (rethrow)
                    throw e;
            }
        }

		public object EvalTuple(object command) {
			EvalStack s = EvalStack.CurrentThreadEvalStack;
#if CALLHANDLERERROR_KLUGE
			callHandlerError = null;
#endif

			if (s.IsEmpty)
				EvalStack.CurrentThreadEvalStack.Push(Namespace.RootNamespace.CheckFbinding("eval"), new object[] { command });
			Expression e = Parser.Parse(command).Simplify(null, false);
			EvalStack.CurrentThreadEvalStack.SetExpression(e, null);
			//replOut.Write(parser.Parse(command,parser));
			//replOut.NewLine();

			// We got this far.  Take the string and tuple form of the command
			// off of the stack.
			//EvalStack.Clear();
			object result = e.Eval(null, EvalStack.CurrentThreadEvalStack);
			Namespace.DefineManifestConstant("<>", result);
			TopLevel top = TopLevel.CurrentThreadTopLevel;
			if (TopLevel.SingleStepMode)
				top.BreakPoint(new ReturnValuePlaceholder(result, e), null);
			if (top.debugger!=null) {
				top.debugger.threadBeingDebugged = null;  // Otherwise it tries to abort the evalthread when we close it
                top.debugger.Invoke((VoidThunk)delegate() { top.debugger.Close(); });
				//top.debugger.Close();
			}
			EvalStack.CurrentThreadEvalStack.ClearForGC();
			return result;
		}

		public void Prompt() {
			replOut.NewLine();
			replOut.stream.Flush();
			replOut.stream.Write(promptString);
			replOut.stream.Flush();
		}

		public double EvalString(string command, bool stepMode, bool inspectMode, int filePosition, bool print) {
			long startTicks=0, elapsedTicks;

			EvalStack.CurrentThreadEvalStack.Clear();
			EvalStack.CurrentThreadEvalStack.Push(((GlobalVariable)Namespace.RootNamespace.Lookup(sEval, false, true, true)).value, new object[] {command});

			TopLevel.SingleStepMode = stepMode;

			if (lowLevelDebug) {
				try {
					replOut.NewLine();
					rawOut.UnindentEndOfBuffer();
					startTicks = Process.GetCurrentProcess().UserProcessorTime.Ticks;
					replOut.stream.WriteLine("=> Namespace {0}", Namespace.CurrentNamespace.name);
					Reader r = new Reader(new System.IO.StringReader(command), filePosition);
					Reader.currentReader.Value = r;
					if (evalStart!=null)
						evalStart();
					object result = EvalTuple(r.Read());
					elapsedTicks = Process.GetCurrentProcess().UserProcessorTime.Ticks - startTicks;
					if (inspectMode)
						Inspector.Inspect(result);
					int outCount = 100;
					replOut.Write(result, ref outCount);
					replOut.NewLine();
					Prompt();
				} finally {
					thread = null;
					//if (debugger != null)
					//	debugger.Close();
					if (idle != null)
						idle();
					if (evalEnd != null)
						evalEnd();
				}
			}else {
				try {
					if (print) {
						replOut.NewLine();
						rawOut.UnindentEndOfBuffer();
					}
					startTicks = Process.GetCurrentProcess().UserProcessorTime.Ticks;
					Reader r = new Reader(new System.IO.StringReader(command), filePosition);
					Reader.currentReader.Value = r;
					if (evalStart!=null)
						evalStart();
					object result = EvalTuple(r.Read());
					elapsedTicks = Process.GetCurrentProcess().UserProcessorTime.Ticks - startTicks;
					if (inspectMode)
						Inspector.Inspect(result);
					int outCount = 100;
					if (print) {
						replOut.Write(result, ref outCount);
						replOut.NewLine();
					}
				} catch (System.Threading.ThreadAbortException) {
					elapsedTicks = Process.GetCurrentProcess().UserProcessorTime.Ticks - startTicks;
					rawOut.Write("Aborted.");
					replOut.NewLine();
				} catch (Exception e) {
					if (e is TargetInvocationException)
						e = ((TargetInvocationException)e).InnerException;
					elapsedTicks = Process.GetCurrentProcess().UserProcessorTime.Ticks - startTicks;
					rawOut.Write("Error: ");
					rawOut.WriteLine(e.Message);
					ShowException(e);
				} finally {
					if (print)
						Prompt();
					thread = null;
					//if (debugger != null)   // this makes the debugger window disappear immediately when an exception is thrown.
					//	debugger.Close();
					if (idle != null)
						idle();
					if (evalEnd != null)
						evalEnd();
				}
			}
            rawOut.Flush();
			MaybeAutoCheck();
			return ((double)elapsedTicks)/System.TimeSpan.TicksPerSecond;
		}

		public static void MaybeAutoCheck() {
			if (AutoCheck) {
				try {
					NamespaceBase packages = (NamespaceBase)(Namespace.RootNamespace.CheckBinding("Packages"));
					Package analysis = (Package)(packages.CheckBinding("Analysis"));
					Procedure.TracedCall(analysis.CheckFbinding("check-namespace"));
				} catch (Exception e) {
					gui.SetStatusMessage("Error in checker: "+e.Message);
				}
			}
		}

		public delegate void WarningHandler(Namespace n);
		public static event WarningHandler WarningsChanged;

		public static void SetWarnings(IDictionary warnings) {
			Namespace.CurrentNamespace.Warnings = warnings;
			if (WarningsChanged != null)
				WarningsChanged(Namespace.CurrentNamespace);
		}

		public object CallWithErrorHandler(Procedure thunk) {
			object result=null;
			if (lowLevelDebug)
				result = Procedure.TracedCall(thunk);
			else {
				try {
					result = Procedure.TracedCall(thunk);
				} catch (Exception e) {
					result=e;
					if (e is TargetInvocationException)
						e = ((TargetInvocationException)e).InnerException;
					ShowException(e);
				}
#if notdef
				finally {
					thread = null;
					if (debugger != null)
						debugger.Close();
				}
#endif
			}
			return result;
		}

		public object CallWithErrorHandler(Procedure thunk, object arg1) {
			object result=null;
			if (lowLevelDebug)
				result = Procedure.TracedCall(thunk, arg1);
			else {
				try {
					result = Procedure.TracedCall(thunk, arg1);
				} catch (Exception e) {
					result=e;
					if (e is TargetInvocationException)
						e = ((TargetInvocationException)e).InnerException;
					ShowException(e);
				}
#if notdef
				finally {
					thread = null;
					if (debugger != null)
						debugger.Close();
				}
#endif
			}
			return result;
		}

		public void LoadStreamWithErrorTrapping(Reader r, object command) {
			Reader.currentReader.Value = r;
			TopLevel.SingleStepMode = false;
			EvalStack.CurrentThreadEvalStack.Clear();
			EvalStack.CurrentThreadEvalStack.Push(command, null);
			try {
				r.SwallowWhitespaceAndComments();
				if (evalStart!=null)
					evalStart();
				while (!r.IsAtEof()) {
					object tuple = r.Read();
					if (TraceLoading)
						replOut.Write(tuple);
					object result = EvalTuple(tuple);
					if (TraceLoading) {
						replOut.stream.Write("\n    \u2192 ");
						replOut.Write(result);
						replOut.NewLine();
					}
					r.SwallowWhitespaceAndComments();
				}
			} catch (System.Threading.ThreadAbortException) {
				rawOut.Write("Aborted.");
				replOut.NewLine();
			} catch (Exception e) {
				if (e is TargetInvocationException)
					e = ((TargetInvocationException)e).InnerException;
				rawOut.Write("Error: ");
				rawOut.WriteLine(e.Message);
				ShowException(e);
			} finally {
				Prompt();
				thread = null;
				//if (debugger != null)
				//	debugger.Close();
				if (idle!=null)
					idle();
				if (evalEnd!=null)
					evalEnd();

			}
			MaybeAutoCheck();
		}

		public static object LoadStream(Reader r) {
			object result =null;

			Reader.currentReader.Value = r;
			r.SwallowWhitespaceAndComments();
			while (!r.IsAtEof()) {
				object tuple = r.Read();
				Expression e = Parser.Parse(tuple).Simplify(null, false);

				result = e.Eval(null, EvalStack.CurrentThreadEvalStack);
				r.SwallowWhitespaceAndComments();
			}
			return result;
		}

		public static object Load(string path, Namespace targetNamespace) {
			return Load(FileToken.GetToken(path), targetNamespace);
		}

		public static object Load(FileToken sourcePath, Namespace targetNamespace) {
			string realLoadPath;
			if (CurrentSourceFileToken==null || CurrentSourceFileToken.Path==null)
				realLoadPath = Path.GetFullPath(sourcePath.Path);
			else
				realLoadPath = Path.Combine(Path.GetDirectoryName(CurrentSourceFileToken.Path), sourcePath.Path);

			return Fluid.Bind(new object[] { 
											   Namespace.currentNamespaceFluid, targetNamespace,
												TopLevel.m_currentSourceDefinition, null,
												TopLevel.m_currentSourceFileToken, FileToken.GetToken(realLoadPath),
											    Reader.currentReader, null
										   }, 
									new Thunk(new LoaderClosure(realLoadPath).Call));
		}

		class LoaderClosure {
			string loadPath;
			public LoaderClosure(string loadPath) {
				this.loadPath = loadPath;
			}

			public object Call() {
				if (Path.GetExtension(loadPath).ToLower()==".dll") {
					Loader.Load(loadPath);
					return null;
				} else {
					StreamReader s = new StreamReader(loadPath);
					object result = null;

					EvalStack.CurrentThreadEvalStack.Push(new TopLevel.LoadFileProc(), new object[] { loadPath });
					if (lowLevelDebug) {
						Namespace.NoteNamespace();
						Reader r = new Reader(new RTFTextReader(s), 0);
						Reader.currentReader.Value = r;
						result = LoadStream(r);
						s.Close();
					} else {
						try {
							Namespace.NoteNamespace();
							Reader r = new Reader(new RTFTextReader(s), 0);
							Reader.currentReader.Value = r;
							result = LoadStream(r);
						} finally {
							s.Close();
						}
					}
					EvalStack.CurrentThreadEvalStack.Pop();
					return result;
				}
			}
		}
	
		[Serializable]
		class LoadFileProc : CallInterfaceProcedure, IArglist, IDocumentable {
			public LoadFileProc() {
				name = Symbol.Intern("load");
			}
			object[] Arglist {
				get {
					return new object[] { Symbol.Intern("fileName"), Symbol.Intern("[namespace]") }	;
				}
			}

			String Documentation {
				get {
					return "Executes all the expressions in the specified file";
				}
			}

			public override object Call(object arg) {
				string filename;

				if (arg==null || !(arg is string || arg is Meta.Symbol))
					throw new ArgumentTypeException("filename",arg, typeof(string));

				filename = arg as string;
				if (filename==null)
					filename = ((Symbol)arg).name;
				return Load(FileToken.GetToken(Loader.FindLoadFile(filename)), Namespace.CurrentNamespace);
			}
			public override object Call(object arg, object packarg) {
				string filename;

				if (arg==null || !(arg is string || arg is Meta.Symbol))
					throw new ArgumentTypeException("filename",arg, typeof(string));

				if (packarg!=null && !(packarg is Meta.Namespace))
					throw new ArgumentTypeException("namespace", packarg, typeof(Meta.Namespace));

				filename = arg as string;
				if (filename==null)
					filename = ((Symbol)arg).name;
				return Load(FileToken.GetToken(Loader.FindLoadFile(filename)), (packarg==null)?Namespace.CurrentNamespace:(Namespace)packarg);
			}
		}

		public static object LaunchFormInternal(Type formType, Object[] constructorArgs, bool show) {
			Form f = (Form)formType.InvokeMember(null, BindingFlags.CreateInstance, null, null, constructorArgs);
			if (show)
				f.Show();
			return f;
		}

		private delegate object RemoteConstructor(Type t, Object[] a, bool show);
		public static Form LaunchForm(Type formType) {
			return LaunchForm(formType, new object[0]);
		}
		public static Form LaunchForm(Type formType, Object[] constructorArgs) {
			return (Form)gui.Invoke(new RemoteConstructor(LaunchFormInternal), new object[] { formType, constructorArgs, true});
		}
		public static Form LaunchForm(Type formType, Object[] constructorArgs, bool show) {
			return (Form)gui.Invoke(new RemoteConstructor(LaunchFormInternal), new object[] { formType, constructorArgs, show});
		}

		static IList tracedProcedures = new WeakList();

		public static void SetTrace(Procedure p) {
			if (p.Traced)
				return;
			p.SetTraced(true);
			lock (tracedProcedures) {
				tracedProcedures.Add(p);
			}
		}

		public static void ClearTrace(Procedure p) {
			if (!p.Traced)
				return;
			p.SetTraced(false);
			lock (tracedProcedures) {
				tracedProcedures.Remove(p);
			}
		}

		public static void ToggleTrace(Procedure p) {
			if (p.Traced)
				ClearTrace(p);
			else
				SetTrace(p);
		}

		public static void ClearAllTraces() {
			lock (tracedProcedures) {
				foreach (Procedure p in tracedProcedures)
					p.SetTraced(false);
				tracedProcedures.Clear();
			}
		}

		public static bool TracesSet {
			get {
				return tracedProcedures.Count>0;
			}
		}

		static IList breakpoints = new WeakList();

		public static void SetBreakpoint(object o) {
			Procedure p = o as Procedure;
			Expression e = o as Expression;

			if (p!=null) {
				if (p.Breakpointed)
					return;
				p.SetBreakpointed(true);
				lock (breakpoints) {
					breakpoints.Add(p);
				}
			} else {
				if (e.executionMode.Breakpoint)
					return;
				e.executionMode.Breakpoint=true;
				lock (breakpoints) {
					breakpoints.Add(e);
				}
			}
		}

		public static void ClearBreakpoint(object o) {
			Procedure p = o as Procedure;
			Expression e = o as Expression;

			if (p!=null) {
				if (!p.Breakpointed)
					return;
				p.SetBreakpointed(false);
				lock (breakpoints) {
					breakpoints.Remove(p);
				}
			} else {
				if (!e.executionMode.Breakpoint)
					return;
				e.executionMode.Breakpoint = false;
				lock (breakpoints) {
					breakpoints.Remove(e);
				}
			}
		}

		public static void ToggleBreakpoint(object o) {
			Procedure p = o as Procedure;
			Expression e = o as Expression;

			if (p!=null) {
				if (p.Breakpointed)
					ClearBreakpoint(p);
				else
					SetBreakpoint(p);
			} else {
				if (e.executionMode.Breakpoint)
					ClearBreakpoint(e);
				else
					SetBreakpoint(e);
			}
		}

		public static void ClearAllBreakpoints() {
			lock (breakpoints) {
				foreach (object o in breakpoints) {
					if (o!=null)
						ClearBreakpoint(o);
				}
			}
		}

		public static bool BreakpointsSet {
			get {
				return breakpoints.Count>0;
			}
		}

		public class FromDefinitionParser : Parser, IArglist, IDocumentable {
			class Closure {
				object exp;
				Parser parser;
				public Closure(object exp, Parser parser) {
					this.exp = exp;
					this.parser = parser;
				}
				public object ContinueParse() {
					return parser.Parse(exp,parser);
				}
			}
			object[] IArglist.Arglist {
				get {
					return new object[] {Symbol.Intern("search-string"), Symbol.Intern("expression") };
				}
			}

			public override object IndentStyle {
				get {
					return Symbol.sBody;
				}
			}

			public override Expression Parse(object list, Parser subparser) {
				IList l = (IList) list;
				if (l.Count!=3) {
					object[] lis = new object[l.Count];
					l.CopyTo(lis,0);
					throw new ArgumentCountException(lis, ((IArglist)this).Arglist);
				}
				object def = l[1];
				object exp = l[2];

				if (!(def is string))
					def = Writer.WriteToString(def);

				return (Expression)(Fluid.Bind(new object[] { Meta.TopLevel.m_currentSourceDefinition, def }, new Meta.Thunk(new Closure(exp, subparser).ContinueParse)));
			}
		}
	}
}